package com.evil.application.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evil.application.enums.form.control.ControlCodeEnum;
import com.evil.application.mapper.ApplicationJudgeMapper;
import com.evil.application.mapper.FormControlOptionMapper;
import com.evil.application.pojo.bo.application.judge.ApplicationJudgeBO;
import com.evil.application.pojo.entity.ApplicationJudge;
import com.evil.application.pojo.entity.FormControlOption;
import com.evil.application.service.ApplicationJudgeService;
import com.evil.application.util.ApplicationUtil;
import com.evil.application.util.MatchingUtil;
import com.evil.common.application.dto.SourceDTO;
import com.evil.common.application.dto.judge.*;
import com.evil.common.application.enums.judge.JudgeSignEnum;
import com.evil.common.application.enums.judge.JudgeSourceTypeEnum;
import com.evil.common.application.enums.judge.JudgeTypeEnum;
import com.evil.common.core.enums.BaseEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.StreamUtil;
import com.evil.common.family.enums.RCodeEnum;
import com.evil.common.core.enums.SwitchEnum;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 应用判断条件表
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Slf4j
@RestController
@AllArgsConstructor
public class ApplicationJudgeServiceImpl extends ServiceImpl<ApplicationJudgeMapper, ApplicationJudge> implements ApplicationJudgeService {

    private final FormControlOptionMapper formControlOptionMapper;

    private final ApplicationLogServiceImpl logService;

    /**
     * 保存
     *
     * @param judgesDTO judgeSourceDTO
     */
    @Override
    public void saveJudges(JudgesDTO judgesDTO) {
        Long enterpriseId = judgesDTO.getEnterpriseId();
        List<JudgeOr> judgeOrList = judgesDTO.getJudgeOrs();
        List<ApplicationJudge> judges = this.baseMapper.findBySource(judgesDTO);
        Map<Long, ApplicationJudge> judgeMap = StreamUtil.toMapK(judges, ApplicationJudge::getJudgeId);

        List<Long> ids = new ArrayList<>();
        judgeOrList.forEach(or -> {
            if (Objects.isNull(or.getControlId())) or.setControlId(ApplicationUtil.DEFAULT_CONTROL_ID);
            if (Objects.isNull(or.getJudgeSign())) or.setJudgeSign(JudgeSignEnum.EQUALS.getId());
            ApplicationJudge judgeOr;
            if (null != or.getJudgeId()) {
                if (!judgeMap.containsKey(or.getJudgeId())) {
                    throw new BusinessException(BaseEnum.ERROR_PARAMETER, "条件信息错误");
                }
                ids.add(or.getJudgeId());
                judgeOr = this.copyProp(judgesDTO, or, judgeMap.get(or.getJudgeId()));
            } else {
                judgeOr = this.copyProp(judgesDTO, or, new ApplicationJudge());
                judgeOr.setEnterpriseId(enterpriseId);
            }
            judgeOr.setJudgeType(JudgeTypeEnum.OR.getId());
            logService.saveAndCheck(this.baseMapper, ApplicationJudge::getJudgeId, judgeOr);

            or.getJudgeAnd().forEach(and -> {
                ApplicationJudge judgeAnd;
                if (null != and.getJudgeId()) {
                    if (!judgeMap.containsKey(and.getJudgeId())) {
                        throw new BusinessException(BaseEnum.ERROR_PARAMETER, "条件信息错误");
                    }
                    MatchingUtil.check(and.getMatchingType(), and.getValues(), v -> v, "条件动态类型错误");

                    ids.add(and.getJudgeId());
                    judgeAnd = this.copyProp(judgesDTO, and, judgeMap.get(and.getJudgeId()));
                } else {
                    judgeAnd = this.copyProp(judgesDTO, and, new ApplicationJudge());
                    judgeAnd.setEnterpriseId(enterpriseId);
                }
                judgeAnd.setJudgeType(JudgeTypeEnum.AND.getId());
                judgeAnd.setParentJudgeId(judgeOr.getJudgeId());
                logService.saveAndCheck(this.baseMapper, ApplicationJudge::getJudgeId, judgeAnd);
            });
        });

        // 处理已失效的
        List<ApplicationJudge> deleted = judges.stream()
                .filter(e -> !ids.contains(e.getJudgeId()))
                .peek(e -> e.setIsDeleted(SwitchEnum.YES.getId()))
                .collect(Collectors.toList());
        logService.saveAndCheck(this.baseMapper, ApplicationJudge::getJudgeId, deleted);
    }

    /**
     * 复制
     *
     * @param copyJudgeReqDTO copyJudgeReqDTO
     */
    @Override
    public void copyJudges(CopyJudgeReqDTO copyJudgeReqDTO) {
        JudgeSourceDTO source = copyJudgeReqDTO.getSource();
        JudgeSourceDTO target = copyJudgeReqDTO.getTarget();

        List<ApplicationJudge> sourceJudgeList = this.baseMapper.findBySource(source);
        List<ApplicationJudge> targetJudgeList = this.baseMapper.findBySource(target);

        // id 映射
        Map<Long, ApplicationJudge> sourceJudgeSourceMap = StreamUtil.toMapK(sourceJudgeList, ApplicationJudge::getJudgeId);
        Map<Long, ApplicationJudge> targetJudgeSourceMap = StreamUtil.toMapK(targetJudgeList, ApplicationJudge::getJudgeId);

        // 层级分类 （只有两层）
        List<ApplicationJudgeBO> sourceJudgeTree = ApplicationUtil.classifyJudge(sourceJudgeList);

        // 编辑/新增
        sourceJudgeTree.forEach(or -> {
            // 或条件
            ApplicationJudge orCopy = this.copyJudge(ApplicationUtil.DEFAULT_PARENT_JUDGE_ID, or, targetJudgeSourceMap, copyJudgeReqDTO);
            if (CollectionUtil.isNotEmpty(or.getChildren())) {
                // 且条件
                or.getChildren().forEach(and -> this.copyJudge(orCopy.getJudgeId(), and, targetJudgeSourceMap, copyJudgeReqDTO));
            }
        });

        // 删除
        targetJudgeList.forEach(judge -> {
            if (!sourceJudgeSourceMap.containsKey(judge.getJudgeId())) {
                judge.setIsDeleted(SwitchEnum.YES.getId());
                logService.saveAndCheck(this.baseMapper, ApplicationJudge::getJudgeId, judge);
            }
        });
    }

    /**
     * 获取条件列表
     *
     * @param source source
     * @return JudgeOr 列表
     */
    @Override
    public List<JudgeOr> findDTOBySource(JudgeSourceDTO source) {
        return this.judgeOrs(this.baseMapper.findBySource(source));
    }

    /**
     * 获取条件列表
     *
     * @param sources sources
     * @return JudgeOr 列表
     */
    @Override
    public Map<JudgeSourceDTO, List<JudgeOr>> findDTOMapBySources(Collection<JudgeSourceDTO> sources) {
        List<ApplicationJudge> judges = this.baseMapper.findBySources(sources);
        return StreamUtil.transMapV(StreamUtil.groupK(judges, e -> new JudgeSourceDTO(e.getSourceType(), e.getSourceId())),
                list -> this.judgeOrs(this.toDto(list)));
    }

    /**
     * 获取条件列表
     *
     * @param sourceTypeEnum sourceTypeEnum
     * @param sourceIds      sourceIds
     * @return Map
     */
    @Override
    public Map<Long, List<JudgeOr>> findMapBySourceIds(JudgeSourceTypeEnum sourceTypeEnum, List<Long> sourceIds) {
        List<JudgeSourceDTO> sources = sourceIds.stream().map(e -> new JudgeSourceDTO(sourceTypeEnum, e)).collect(Collectors.toList());
        return this.baseMapper.findBySources(sources).stream()
                .collect(Collectors.groupingBy(ApplicationJudge::getSourceId,
                        // 转DTO
                        Collectors.collectingAndThen(Collectors.toList(), this::judgeOrs)));
    }

    /**
     * 转换条件列表
     *
     * @param judges judges
     * @return JudgeOr
     */
    @Override
    public List<JudgeOr> judgeOrs(Collection<? extends Judge> judges) {
        List<JudgeAnd> ands = StreamUtil.transListFT(judges, e -> e instanceof JudgeAnd, e -> (JudgeAnd) e);
        Map<Long, List<JudgeAnd>> parentMap = StreamUtil.groupK(ands, JudgeAnd::getParentJudgeId);
        return judges.stream()
                .filter(e -> e instanceof JudgeOr)
                .map(e -> {
                    JudgeOr or = (JudgeOr) e;
                    or.setJudgeAnd(parentMap.getOrDefault(or.getJudgeId(), new ArrayList<>()));
                    return or;
                }).collect(Collectors.toList());
    }

    /**
     * 转DTO
     *
     * @param judges judges
     * @return Judge 列表
     */
    @Override
    public List<? extends Judge> toDto(Collection<ApplicationJudge> judges) {
        return judges.stream().map(e -> {
            JudgeTypeEnum judgeTypeEnum = JudgeTypeEnum.findById(e.getJudgeType());
            Judge judge = BeanUtil.copyProperties(e, judgeTypeEnum.getJClass(), ApplicationJudge.COL_VALUES);
            judge.setValues(JSON.parseArray(e.getValues(), String.class));
            return judge;
        }).collect(Collectors.toList());
    }

    /**
     * 检查 条件判断
     *
     * @param judgeOr        judgeOr
     * @param controlCodeMap controlCodeMap
     * @param controlIdMap   需要替换的控件id
     */
    @Override
    public void checkJudge(List<JudgeOr> judgeOr, Map<Long, String> controlCodeMap, Map<Long, Long> controlIdMap) {
        if (CollectionUtil.isNotEmpty(judgeOr)) {
            judgeOr.forEach(or -> {
                // 检查且条件
                or.getJudgeAnd().forEach(and -> this.checkJudge(and, controlCodeMap, controlIdMap));
            });
        }
    }

    /**
     * 检查 条件 配置
     *
     * @param judge          judge
     * @param controlCodeMap 控件code映射
     * @param controlIdMap   需要替换的控件id
     */
    private void checkJudge(Judge judge, Map<Long, String> controlCodeMap, Map<Long, Long> controlIdMap) {
        if (null == judge.getControlId()) {
            throw new BusinessException(BaseEnum.ERROR_PARAMETER, "条件表单控件不能为空");
        }
        if (null != controlCodeMap) {
            if (!controlCodeMap.containsKey(judge.getControlId())) {
                throw new BusinessException(BaseEnum.ERROR_PARAMETER, "条件表单控件不存在");
            }
            judge.setControlCode(controlCodeMap.get(judge.getControlId()));
        }
        ControlCodeEnum controlCodeEnum = ControlCodeEnum.findByCode(judge.getControlCode());
        if (null != controlIdMap) {
            if (!controlIdMap.containsKey(judge.getControlId())) {
                throw new BusinessException(BaseEnum.ERROR_PARAMETER, "条件表单控件不存在");
            }
            judge.setControlId(controlIdMap.get(judge.getControlId()));
            if (controlCodeEnum.hasOptions()) {
                // 如果是条件是选项值
                List<FormControlOption> options = formControlOptionMapper.findByControlId(judge.getControlId(), true);
                Map<Long, Long> sourceOptionMap = StreamUtil.toMapKV(options, FormControlOption::getSourceOptionId, FormControlOption::getOptionId);

                judge.setValues(judge.getValues().stream()
                        .map(e -> {
                            Long optionId;
                            try {
                                optionId = Long.valueOf(e);
                            } catch (NumberFormatException numberFormatException) {
                                throw new BusinessException(BaseEnum.ERROR, "条件值错误");
                            }
                            if (!sourceOptionMap.containsKey(optionId)) {
                                throw new BusinessException(BaseEnum.ERROR, "条件值错误");
                            }
                            return Long.toString(sourceOptionMap.get(optionId));
                        })
                        .toList());
            }
        }

        // 检查 判断符号参数
        JudgeSignEnum judgeSignEnum = JudgeSignEnum.isExistById(judge.getJudgeSign());

        // 检查 该控件是否支持 该判断符号
        if (!controlCodeEnum.getJudgeSigns().containsKey(judgeSignEnum)) {
            throw new BusinessException(BaseEnum.ERROR_PARAMETER, "条件表单控件不支持该判断符");
        }
    }

    /**
     * 判断条件列表转DTO
     *
     * @param judges judges
     * @return List
     */
    private List<JudgeOr> judgeOrs(List<ApplicationJudge> judges) {
        return this.judgeOrs(this.toDto(judges));
    }

    /**
     * 创建判断条件
     *
     * @param sourceDTO sourceDTO
     * @param judge     judge
     * @return ApplicationJudge
     */
    private ApplicationJudge copyProp(SourceDTO sourceDTO, Judge judge, ApplicationJudge applicationJudge) {
        if (Objects.isNull(judge.getControlId())) {
            throw new BusinessException(BaseEnum.ERROR_PARAMETER, "条件控件id不能为空");
        }
        applicationJudge.setSourceId(sourceDTO.getSourceId());
        applicationJudge.setSourceType(sourceDTO.getSourceType());
        applicationJudge.setControlId(judge.getControlId());
        applicationJudge.setControlCode(judge.getControlCode());
        applicationJudge.setJudgeSign(judge.getJudgeSign());
        applicationJudge.setMatchingType(judge.getMatchingType());
        applicationJudge.setTargetId(judge.getTargetId());
        applicationJudge.setSourceJudgeId(0L);
        applicationJudge.setValues(JSON.toJSONString(judge.getValues()));

        return applicationJudge;
    }

    /**
     * 复制
     *
     * @param parentJudgeId        parentJudgeId
     * @param judge                judge
     * @param targetJudgeSourceMap targetJudgeSourceMap
     * @param copyJudgeReqDTO      copyJudgeReqDTO
     * @return ApplicationJudge
     */
    private ApplicationJudge copyJudge(Long parentJudgeId,
                                       ApplicationJudgeBO judge,
                                       Map<Long, ApplicationJudge> targetJudgeSourceMap,
                                       CopyJudgeReqDTO copyJudgeReqDTO) {
        Long enterpriseId = copyJudgeReqDTO.getEnterpriseId();
        JudgeSourceDTO target = copyJudgeReqDTO.getTarget();
        Map<Long, Long> sourceControlIdMap = copyJudgeReqDTO.getSourceControlIdMap();
        Map<Long, Long> targetControlIdMap = copyJudgeReqDTO.getTargetControlIdMap();
        Map<Long, Long> sourceOptionIdMap = copyJudgeReqDTO.getSourceOptionIdMap();
        // 编辑/新增
        ApplicationJudge copy = this.copy(judge, targetJudgeSourceMap.getOrDefault(judge.getJudgeId(), null));
        copy.setEnterpriseId(enterpriseId);
        copy.setSourceType(target.getSourceType());
        copy.setSourceId(target.getSourceId());
        copy.setParentJudgeId(parentJudgeId);

        // 替换控件id（需要替换控件id && 替换规则不存在 && 当前不是默认控件id）
        if (Objects.nonNull(sourceControlIdMap) && !sourceControlIdMap.containsValue(copy.getControlId()) && !ApplicationUtil.DEFAULT_CONTROL_ID.equals(copy.getControlId())) {
            if (!sourceControlIdMap.containsKey(copy.getControlId())) {
                throw new BusinessException(RCodeEnum.APPLICATION_FORM_CONTROL_NOT_EXIST);
            }
            copy.setControlId(sourceControlIdMap.get(copy.getControlId()));
        }

        // 替换目标id（需要替换控件id && 替换规则不存在 && 当前不是默认控件id）
        if (Objects.nonNull(targetControlIdMap) && !targetControlIdMap.containsValue(copy.getTargetId()) && !ApplicationUtil.DEFAULT_CONTROL_ID.equals(copy.getTargetId())) {
            if (!targetControlIdMap.containsKey(copy.getTargetId())) {
                throw new BusinessException(RCodeEnum.APPLICATION_FORM_CONTROL_NOT_EXIST);
            }
            copy.setTargetId(targetControlIdMap.get(copy.getTargetId()));
        }
        // 替换选项id（需要替换选项id && 且条件 && 选项控件）
        if (Objects.nonNull(sourceOptionIdMap) && JudgeTypeEnum.AND.getId() == copy.getJudgeType()) {
            ControlCodeEnum controlCodeEnum = ControlCodeEnum.findByCode(copy.getControlCode());
            // 选项
            if (controlCodeEnum.hasOptions()) {
                List<Long> optionIds = JSON.parseArray(copy.getValues(), Long.class);
                List<Long> copyOptionIds = optionIds.stream().map(e -> {
                    if (!sourceOptionIdMap.containsKey(e)) {
                        throw new BusinessException(RCodeEnum.APPLICATION_FORM_CONTROL_OPTION_NOT_EXIST);
                    }
                    return sourceOptionIdMap.get(e);
                }).collect(Collectors.toList());
                copy.setValues(JSON.toJSONString(copyOptionIds));
            }
        }
        logService.saveAndCheck(this.baseMapper, ApplicationJudge::getJudgeId, copy);

        if (!judge.getSourceJudgeId().equals(copy.getJudgeId())) {
            // 更新源信息
            judge.setSourceJudgeId(copy.getJudgeId());
            ApplicationJudge applicationJudge = BeanUtil.copyProperties(judge, ApplicationJudge.class);
            logService.saveAndCheck(this.baseMapper, ApplicationJudge::getJudgeId, applicationJudge);
        }
        return copy;
    }

    /**
     * 复制
     *
     * @param source source
     * @param target target
     * @return ApplicationJudge
     */
    private ApplicationJudge copy(ApplicationJudge source, ApplicationJudge target) {
        if (null == target) {
            target = new ApplicationJudge();
        }
        target.setControlId(source.getControlId());
        target.setControlCode(source.getControlCode());
        target.setJudgeSign(source.getJudgeSign());
        target.setMatchingType(source.getMatchingType());
        target.setJudgeType(source.getJudgeType());
        target.setTargetId(source.getTargetId());
        target.setTargetId(source.getTargetId());
        target.setSourceJudgeId(source.getJudgeId());
        target.setValues(source.getValues());

        return target;
    }
}
